package cn.edu.hit.kg.entity;

import cn.edu.hit.CacheManager;
import cn.edu.hit.CachedObjectPool;
import cn.edu.hit.ICachedObjectPool;
import cn.edu.hit.kg.FunctionUtil;
import cn.edu.hit.kg.data.Node;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
//import org.neo4j.driver.v1.types.Node;

import java.util.*;
import java.util.stream.Collectors;

public class Feature extends BaseNode implements ISignalMatch {
    public Feature(Node node) {
        super(node);
    }

    @Override
    public boolean matchSignal(List<Signal> signals, long timestamp, String unitId) {
//        NeoDriver driver = new NeoDriver();
        if (lastTimestamp.get(unitId, 0L) == timestamp) {
            return isMatched;
        }
        lastTimestamp.push(unitId, timestamp);

        if (componentList == null) {
            componentList = getAllSubNodes(this.getId())
                    .map(id -> nodeCache.get(id))
                    .filter(nd -> nd.getNode().hasLabel("Component"))
                    .map(x -> (Component) x)
                    .collect(Collectors.toList());
        }

        if (this.isAuto()) {
            //自动诊断
            if (StringUtils.startsWith(this.getString("opertion"), "js:")) {
                //是表达式，不必检查测点  TODO 使用正则提取测点信息进行匹配

            } else {
                //是函数，需检查部件信息，如不存在，返回unknown，并提示
                if (componentList == null || componentList.size() == 0) {
                    unknown = true;
                    isMatched = false;
                    return isMatched;
                }
//               else{
//
//               }
            }
        } else {
            //人工诊断
            Optional<Signal> signalOption = signals.stream().filter(s ->
                    (s.getComponent() == null || !s.getComponent().containsKey("id"))
                            && StringUtils.equalsIgnoreCase("manmal" + this.getId(), s.getFeatureType())).findAny();
            if (signalOption.isPresent()) {
                unknown = false;
                return StringUtils.contains("1", signalOption.get().getValue().toString());
            } else {
                unknown = true;
                isMatched = false;
                return isMatched;
            }
        }


        List<Signal> featureNameMatched = signals.parallelStream()
                //Step1. 特征类型判断
                .filter(signal -> StringUtils.equals(signal.getFeatureType(), this.getString("name")))
                .collect(Collectors.toList());

        List<Signal> matchedSignal = featureNameMatched.parallelStream()
                //Step2. 部件判断
                .filter(signal -> componentList.size() != 0 && signal.getComponent() != null && signal.getComponent().containsKey("id") &&
                        componentList.stream().anyMatch(component ->
                                StringUtils.equals("" + component.getId(), signal.getComponent().get("id").toString()))
                )
                .collect(Collectors.toList());
        //Step3. 函数判断
        if (matchedSignal.size() == 0) {
            unknown = true;
            isMatched = false;
        } else {
            try {
                isMatched = matchedSignal.stream().anyMatch(signal -> FunctionUtil.doMatch(signal, this, signals, unitId));
            } catch (NumberFormatException ex) {
//                ex.printStackTrace();
                logger.error("In Feature.java," + this.getId() + "," + ex.getMessage());
//                System.err.println("In Feature.java,"+this.getId()+","+ex.getMessage());
                unknown = true;
                isMatched = false;
            }
        }

//        isMatched = signals.parallelStream()
//                //Step1. 特征类型判断
//                .filter(signal -> signal.getFeatureType().equals(this.getString("name")))
//                //Step2. 部件判断
//                .filter(signal ->
//                    driver.query("start f=node($fid),c=node($cid) " +
//                            "match p=(f)-[*]->(c) return count(p) as cp",
//                            parameters("fid",this.getId(),"cid",Integer.parseInt( signal.getComponent().get("id").toString()))
//                    ).single().get("cp").asInt() > 0)
//                //Step3. 函数判断
//                .anyMatch(signal -> FunctionUtil.doMatch(signal,this));

        // System.out.println(this.getId()+","+this.get("name", Value::asString)+":"+isMatched+","+unknown);

        return isMatched;
    }

    @Override
    public MatchResultType matchByConclusions(Map<Long, Boolean> conclusions) {
        if (conclusions.containsKey(this.getId())){
            return conclusions.get(this.getId())? MatchResultType.TRUE:MatchResultType.FALSE;
        }
        return MatchResultType.UNKNOWN;
    }

    @Override
    public MatchTrack trackByConclusions(Map<Long, Boolean> conclusions) {
        MatchTrack track = new MatchTrack();
        track.node = this;
        if (conclusions.containsKey(this.getId())){
            track.result = conclusions.get(this.getId())? MatchResultType.TRUE:MatchResultType.FALSE;
        }
        return track;
    }

    @Override
    public List<Integer> getOutIds() {
        return Arrays.stream(this.get("outId", stringTransfer).split(","))
                .filter(x -> StringUtils.isNumericSpace(x) && StringUtils.isNotBlank(x))
                .mapToInt(Integer::parseInt)
                .filter(x -> x != 0)
                .boxed()
                .collect(Collectors.toList());
    }

    private boolean isMatched;
    private boolean unknown = false;

    private final CacheManager cacheManager = CacheManager.getInstance();
    private ICachedObjectPool<String, Long> lastTimestamp = cacheManager.createCachedObjectPool("Feature.lastTimestamp." + this.getId());
    private ICachedObjectPool<String, HashSet<String>> channelIds = cacheManager.createCachedObjectPool("Feature.channelIds." + this.getId());

    private List<Component> componentList = null;
//    public Set<String> channelIds = new HashSet<>();

    public void setMatched(boolean matched) {
        isMatched = matched;
    }

    public boolean isMatched() {
        return isMatched;
    }

    @Override
    public boolean isUnKnown() {
        return unknown;
    }

    @Override
    public List<ISignalMatch> getOutMatches() {
        return Collections.emptyList();
    }

    @Override
    public List<Long> getSubMatched() {
        return Collections.emptyList();
    }

    @Override
    public List<Long> getSubUnknown() {
        return Collections.emptyList();
    }

    @Override
    public HashSet<String> getChannelIds(String unitId) {
        return channelIds.get(unitId, Sets.newHashSet());
    }

    @Override
    public Set<Feature> getFeatureSet() {
        return Collections.singleton(this);
    }

    public ICachedObjectPool<String, HashSet<String>> getChannelIds() {
        return channelIds;
    }

    public boolean isAuto() {
        return node.get("type").toString().equalsIgnoreCase("auto");
    }

    public void setComponents(List<Component> components) {
        this.componentList = components;
    }

    public List<Component> getComponents() {
        return componentList;
    }
}
